import 'dart:async';
import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logger/logger.dart';
import 'package:merchants/providers/session_provider.dart';
import 'package:retry/retry.dart';

import 'package:merchants/services/app.dart';
import 'package:merchants/providers/dio_provider.dart';
import 'package:merchants/providers/providers.dart';

class ApiException implements Exception {
  final String message;
  final int? statusCode;

  ApiException(this.message, {this.statusCode});

  @override
  String toString() => 'ApiException: $message (Status: $statusCode)';
}

class BaseService {
  final Dio _dio;
  final Duration _timeout;
  final Logger _logger;
  final Ref _ref;

  static const int _maxRetries = 3;

  BaseService({Dio? dio, Duration? timeout, Logger? logger, required Ref ref})
    : _dio = dio ?? ref.read(dioProvider),
      _timeout = timeout ?? const Duration(seconds: 8),
      _logger = logger ?? Logger(),
      _ref = ref {
      // 确保全局 AppServices 已初始化；忽略返回 Future
      // ignore: unawaited_futures
      AppServices.initialize();
      }

  /// ────────────────────────────── 通用 Header ──────────────────────────────
  Map<String, String> get _defaultHeaders => {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'App-Version': AppServices.appVersion,
  };

  /// 登录后需要携带的三大头，任意缺失直接抛 401 异常
  Future<Map<String, String>> _getAuthHeaders() async {
    final session = _ref.read(sessionProvider).value;
    final token = session?.token;
    final unionid = session?.unionid;
    final merchantId = session?.merchantId;

    if (token == null) throw ApiException('Token 为空，无法发起请求', statusCode: 401);
    if (unionid == null) {
      throw ApiException('UnionId 为空，无法发起请求', statusCode: 401);
    }
    if (merchantId == null) {
      throw ApiException('MerchantId 为空，无法发起请求', statusCode: 401);
    }

    return {
      'x-token': 'Bearer $token',
      'x-unionid': unionid,
      'x-merchantid': merchantId,
    };
  }

  /// 给 query / body 自动追加时间戳，后端用于防重放（可选）
  Map<String, dynamic> _processParams(
    Map<String, dynamic>? params, {
    bool includeTimestamp = true,
  }) {
    final processed = params == null ? <String, dynamic>{} : Map.of(params);
    if (includeTimestamp) {
      processed['timestamp'] = DateTime.now().millisecondsSinceEpoch.toString();
    }
    return processed;
  }

  /// ────────────────────────────── 统一请求入口 ──────────────────────────────
  Future<T> _request<T>(
    String method, // GET / POST / PUT / DELETE
    String endpoint, {
    Map<String, dynamic>? queryParams,
    Map<String, dynamic>? body,
    T Function(Map<String, dynamic>)? parser,
    bool requireToken = true,
    bool includeTimestamp = true,
    Map<String, String>? additionalHeaders,
  }) async {
    try {

      final headers = {
        ..._defaultHeaders,
        if (requireToken) ...(await _getAuthHeaders()),
        if (additionalHeaders != null) ...additionalHeaders,
      };

      final response = await retry(
        () => _dio.request(
          endpoint, // AppServices.baseUrl 已在 dioProvider 里设置
          options: Options(
            method: method,
            headers: headers,
            sendTimeout: _timeout,
            receiveTimeout: _timeout,
            responseType: ResponseType.json,
          ),
          queryParameters: _processParams(
            queryParams,
            includeTimestamp: includeTimestamp,
          ),
          data: body,
        ),
        maxAttempts: _maxRetries,
        onRetry: (e) => _logger.w('请求重试: $e'),
        retryIf:
            (e) =>
                e is DioException &&
                e.type == DioExceptionType.connectionError, // 仅连接错误才自动重试
      );

      return _handleResponse(response, parser: parser);
    } catch (e) {
      _logger.e('请求失败: $e');
      rethrow;
    }
  }

  /// ────────────────────────────── 统一响应处理 ──────────────────────────────
  Future<T> _handleResponse<T>(
    Response response, {
    T Function(Map<String, dynamic>)? parser,
  }) async {
    final status = response.statusCode ?? 0;
    final data = response.data;

    _logger.d('请求 URL: ${response.requestOptions.uri}');
    _logger.d('状态码  : $status');
    _logger.d('响应体  : $data');

    if (status >= 200 && status < 300) {
      // 业务成功
      if (data is Map<String, dynamic>) {
        if (data['code'] != null && data['code'] != 0) {
          // 后端自定义状态码非 0，当作业务错误抛出
          throw ApiException(
            data['message'] ?? '未知错误',
            statusCode: data['code'],
          );
        }
        return parser != null ? parser(data) : data as T;
      } else {
        return parser != null ? parser({'data': data}) : data as T;
      }
    }

    // HTTP 层错误映射
    if (status == 401) {
      throw ApiException('未授权，请重新登录', statusCode: 401);
    } else if (status == 403) {
      throw ApiException('无权限访问资源', statusCode: 403);
    } else if (status == 429) {
      throw ApiException('请求过于频繁，请稍后再试', statusCode: 429);
    } else if (status >= 400 && status < 500) {
      throw ApiException('客户端错误: $data', statusCode: status);
    } else {
      throw ApiException('服务器错误: $data', statusCode: status);
    }
  }

  /// ────────────────────────── 对外公开的四个快捷方法 ─────────────────────────
  Future<T> get<T>(
    String endpoint, {
    Map<String, dynamic>? queryParams,
    T Function(Map<String, dynamic>)? parser,
    bool requireToken = true,
    bool includeTimestamp = true,
    Map<String, String>? additionalHeaders,
  }) => _request(
    'GET',
    endpoint,
    queryParams: queryParams,
    parser: parser,
    requireToken: requireToken,
    includeTimestamp: includeTimestamp,
    additionalHeaders: additionalHeaders,
  );

  Future<T> post<T>(
    String endpoint, {
    Map<String, dynamic>? body,
    Map<String, dynamic>? queryParams,
    T Function(Map<String, dynamic>)? parser,
    bool requireToken = true,
    bool includeTimestamp = true,
    Map<String, String>? additionalHeaders,
  }) => _request(
    'POST',
    endpoint,
    queryParams: queryParams,
    body: _processParams(body, includeTimestamp: includeTimestamp),
    parser: parser,
    requireToken: requireToken,
    includeTimestamp: includeTimestamp,
    additionalHeaders: additionalHeaders,
  );

  Future<T> put<T>(
    String endpoint, {
    Map<String, dynamic>? body,
    Map<String, dynamic>? queryParams,
    T Function(Map<String, dynamic>)? parser,
    bool requireToken = true,
    bool includeTimestamp = true,
    Map<String, String>? additionalHeaders,
  }) => _request(
    'PUT',
    endpoint,
    queryParams: queryParams,
    body: _processParams(body, includeTimestamp: includeTimestamp),
    parser: parser,
    requireToken: requireToken,
    includeTimestamp: includeTimestamp,
    additionalHeaders: additionalHeaders,
  );

  Future<T> delete<T>(
    String endpoint, {
    Map<String, dynamic>? body,
    Map<String, dynamic>? queryParams,
    T Function(Map<String, dynamic>)? parser,
    bool requireToken = true,
    bool includeTimestamp = true,
    Map<String, String>? additionalHeaders,
  }) => _request(
    'DELETE',
    endpoint,
    queryParams: queryParams,
    body: _processParams(body, includeTimestamp: includeTimestamp),
    parser: parser,
    requireToken: requireToken,
    includeTimestamp: includeTimestamp,
    additionalHeaders: additionalHeaders,
  );

  /// ───────────────────────────── Multipart 上传 ─────────────────────────────
  Future<T> multipartPost<T>(
    String endpoint, {
    Map<String, dynamic>? fields,
    List<MultipartFile>? files,
    T Function(Map<String, dynamic>)? parser,
    bool requireToken = true,
    Map<String, String>? additionalHeaders,
  }) async {
    try {
      // 已在构造函数触发初始化

      final headers = {
        ..._defaultHeaders,
        if (requireToken) ...(await _getAuthHeaders()),
        if (additionalHeaders != null) ...additionalHeaders,
      };

      // 使用 Dio 的 FormData 处理文件上传
      final formData = FormData();

      // 普通字段
      fields?.forEach((key, value) {
        formData.fields.add(MapEntry(key, value.toString()));
      });

      // 文件字段
      if (files != null) {
        formData.files.addAll(files.map((f) => MapEntry('files', f)));
      }

      final response = await retry(
        () => _dio.post(
          endpoint,
          options: Options(
            headers: headers,
            sendTimeout: _timeout,
            receiveTimeout: _timeout,
            contentType: Headers.multipartFormDataContentType,
          ),
          data: formData,
        ),
        maxAttempts: _maxRetries,
        onRetry: (e) => _logger.w('Multipart 请求重试: $e'),
        retryIf:
            (e) =>
                e is DioException && e.type == DioExceptionType.connectionError,
      );

      return _handleResponse(response, parser: parser);
    } catch (e) {
      _logger.e('Multipart 请求失败: $e');
      rethrow;
    }
  }
}
